/**
 * Operations to write a display file
 * 
 * The character set used is by default UTF-8
 * Could be easily changed if necessary
 * 
 * @author J.J.G.
 */


package fr.emse.tatiana.display;


import java.io.*;
import java.util.*;
import java.awt.Color;

public class DisplayFileWriter {
	
	private Writer fw;
	String fid;
	String type;
	String encoding;
	boolean curropen;
	boolean currdisp;
	boolean curritem;
	boolean currinfo;
	boolean ioerror;
	
	/**
	 * Create a new DisplayFileWriter
	 * @param fn - the name of the created display file
	 * 
	 * A form of return code can be obtained with ioError()
	 */
	public DisplayFileWriter (String fn)
	{
		encoding = "UTF-8";
		fid = fn;

	    curropen = false;
	    currdisp = false;
	    curritem = false;
	    currinfo = false;
		ioerror = false;
		
		 try {
			    fw = new BufferedWriter(new OutputStreamWriter(
		            new FileOutputStream(fn), encoding));
			    curropen = true;
		 } catch (UnsupportedEncodingException e) {
	    	    System.err.println("Cannot use "+encoding+" for file "+ fid);
			    ioerror = true;
		 } catch (IOException e) {
	    	    System.err.println("Cannot open file "+ fid);
			    ioerror = true;
		 }


	}
	
	public DisplayFileWriter (String fn, String type) {
		this(fn);
		this.type = type;
	}
	
	/**
	 * Internal low level String output operation
	 * Performs the actual writing on file
	 * Do not use, except from output()
	 * @param str : the String to write
	 */
	private void outputClean(String str)
	{
		if (! ioerror)
		{
		    try {
			    fw.write(str);
		    } catch (IOException e) {
	    	    System.err.println("Cannot write to file "+ fid);
			    ioerror = true;
		    }
		}
	}

	/**
	 * Internal low level String output operation
	 * Calls outputClean()
	 * It's an intermediate operation, should some conversions
	 * or corrections be performed on the output String
	 * @param str : the String to write
	 */
	private void output(String str)
	{
		outputClean(str);
	}
	
	/**
	 * Generate an Element with a given tag
	 * @param tagid : the tag, as a String
	 * @param value : the content of the Element, as a String
	 * @return
	 */
	private String tag(String tagid, String value)
	{
		return "<" + tagid + ">" + value + "</" + tagid + ">" ;
	}

	/**
	 * Internal low level Object output operation
	 * This does its best to provide the most appropriate type for
	 * Java Object
	 * @param dtype : the "tatiana type" of the object, as a String
	 * @param val : the value to output, as an Object
	 */
    private void outputZ(String pty, Object val)
    {
    	String str = null;
    	if (val == null)
    		return;
    	if (val instanceof String)
    	{
            str = Ut.toXML((String)val);
    	}
    	else
    	if (val instanceof Color)
    	{
    		if (pty.equals("bgcolor"))
    		{
                str = "<bgcolor>" + Ut.colorName((Color)val) + "</bgcolor>";
    		}
    		else
    		{
                str = "<color>" + Ut.colorName((Color)val) + "</color>";
    		}
    	}
    	else
    	if (val instanceof DisplayCell)
    	{
            str = ((DisplayCell)val).toXMLString();
    	}
    	else
    	if (val instanceof Integer)
    	{
            str = "<integer>" + ((Integer)val).toString() + "</integer>";
    	}
    	else
    	if (val instanceof Long)
    	{
            str = "<long>" + ((Long)val).toString() + "</long>";
    	}
    	else
    	if (val instanceof Boolean)
    	{
            str = "<boolean>" + (((Boolean)val) ? "true" : "false" ) + "</boolean>";
    	}
    	else
    	if (val instanceof DisplayDate)
    	{
            str = ((DisplayDate)val).toXMLString();
    	}
    	else
    	if (val instanceof org.w3c.dom.Node)
    	{
            str = Ut.formatXMLNode((org.w3c.dom.Node) val);
    	}
    	else
    	if (val instanceof DisplayIcon)
    	{
            str = "<icon>" + Ut.toXML(((DisplayIcon)val).iconName()) + "</icon>";
    	}
    	else
    	if (val instanceof File)
    	{
            str = "<file>" + Ut.toXML(((java.io.File)val).toString()) + "</file>";
    	}
    	else
    	if (val instanceof Date)
    	{
            str = "<time><date>" + Ut.toXML(Long.toString(((Date)val).getTime()))+ "</date></time>";
    	}
    	else
    	{
    		str = "#" + Ut.toXML(val.getClass().getName()) + "#" + Ut.toXML(val.toString()) + "#";
    	}
    	if (str != null)
    	{
    		output(str);
    	}
    
    }
    
    private void output(String dtype, Object val)
    {
    	String jt = Ut.getTType(val);
    	if (dtype.equals("any"))
    		dtype = jt;  // Because... any is too general : we use actual value
    	String ct = Ut.mergeTypes(jt, dtype);
    	if (ct.equals(jt))
    	{
    		// Compatible types
    		outputZ(ct, val);
    		return;
    	}
    	// Are the type compatible ?
    	ct = Ut.convertPossible(jt, dtype);
    	if (ct.equals(""))
    	{
    	    System.out.println(" Non compatible types : Requested = "+dtype+"  Actual = "+jt);
    	}
    	outputZ(ct, val);
    }
  
    
    /**
     * Build the file header, and create the
     * first element tag
     *
     */
    private void mkheader()
    {
    	if (! currdisp)
	     {
	    	 currdisp = true;
	    	 output("<?xml version='1.0' encoding='" + encoding + "'?>\n");
	    	 output("<display type='"+
	    			 (type!=null ? Ut.toXML(type) : "")+
	    			 "'>\n"
	    	 );
	     }
    }
    
	public void newItem()
	{
	     if (! curropen)
	    	 return;
	     mkheader();
	     if (curritem)
	     {
	    	 // There already is an item opened ; close it first
	    	 output("  </item>\n");
	     }
	     else
	     {
	    	 curritem = true;
	     }
    	 output("  <item>\n");
	}
	
	public void newInfo(String idt, String dtype, Object val)
	{
		 if (! curropen)
	    	 return;
	     mkheader();
	     if (! curritem)
	     {
	    	 curritem = true;
	    	 output("  <item>\n");
	     }
	     output("    <info name=\""+ Ut.toXML(idt) + "\">");
	     output(dtype, val);
	     output("</info>\n");
	}
	
	// copy pasted from above please adjust changes - G Dyke
	// used so as not to escape xml?
	public void newInfoFromString(String idt, String str) {
		 if (! curropen)
	    	 return;
	     mkheader();
	     if (! curritem)
	     {
	    	 curritem = true;
	    	 output("  <item>\n");
	     }
	     output("    <info name=\""+ Ut.toXML(idt) + "\">");
	     output(str);
	     output("</info>\n");
	}
	
	public void close()
	{
		 if (! curropen)
	    	 return;
	     mkheader();
	     if (curritem)
	     {
	    	 // There already is an item opened ; close it first
	    	 output("  </item>\n");
	    	 curritem = false;
	     }
	     if (currdisp)
	     {
	    	 output("</display>\n");
	    	 currdisp = false;
	     }
	     
	     try {
	         fw.close();
	     } catch (IOException e) {
	    	    System.err.println("Cannot close file "+ fid);
			    ioerror = true;
	     }
	     
	     curropen = false;
		
	}
	
	public boolean ioError()
	{
		return ioerror;
	}
}